package biolabs.mcl

import biolabs.core.Application
import biolabs.mcl.syntax._

object Containment {
	def analyze(ast: ContainmentAST, model: ModelDefinition, app: Application): AnalysisResult[Containment] = {
		// analyze the child type
		val childType = MultiType.analyze(ast.tpe, model, model.typeParameterMap, app) match {
			case AnalysisSuccess(mt) => mt
			case AnalysisError(msg) => return AnalysisError(msg)
		}

		ast match {

			case SingletonContainmentAST(name, _, initExpr, ts) =>
				AnalysisSuccess(SingletonContainment(name, childType, initExpr, ts))

			case MultiContainmentAST(name, _, assoc, ts, cs) =>
				// analyze the types of associated values
				val associatedTypes =  {
					val (suc, err) = assoc map { TypedIdentifier.analyze(_, model, app) } partition { _.successful }
					if(!err.isEmpty)
						return AnalysisError(err map {_.getErrorMessage()} mkString "\n")
					suc map { _.getResult() }
				}

				// determine container type
				val containerSpec = cs match {
					case Some(t) => ContainerSpec.analyze(t, childType, associatedTypes map {_.tpe}) match {
						case AnalysisSuccess(some) => some
						case AnalysisError(msg) => return AnalysisError(msg)
					}
					case None => ContainerSpec.default(childType, associatedTypes map {_.tpe})
				}

				AnalysisSuccess(MultiContainment(name, childType, ts, associatedTypes, containerSpec))
		}
	}
}

abstract class Containment {
	val name: String
	val tpe: MultiType
	val timeScale: Option[(String, String)]
}

/** For containment of a single instance of the specified type
  * (has-a relationship).
  */
case class SingletonContainment private[mcl] (
	override val name: String,
	override val tpe: MultiType,
	val initExpr: String,
	override val timeScale: Option[(String, String)]
) extends Containment

/** For containment of multiple instances of the specified type,
  * in a container.
  */
case class MultiContainment private[mcl] (
	override val name: String,
	override val tpe: MultiType,
	override val timeScale: Option[(String, String)],
	val associatedValues: List[TypedIdentifier],
	val containerSpec: ContainerSpec
) extends Containment {

	def associates(name: String): Boolean =
		associatedValues.exists(_.name == name)

	def associatedValueNamesToNumbers(names: List[String]): List[Int] =
		names map (name => associatedValues.indexWhere(_.name == name)+1)
}